package socketmvc.core.packets.basic.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import socketmvc.core.packets.biz.SockFile;
import socketmvc.core.packets.basic.IFilePacket;
import socketmvc.core.packets.basic.IMultiFilePacket;
import socketmvc.core.packets.basic.JsonBasicPacket;
import socketmvc.core.packets.basic.Protocol;
import socketmvc.core.packets.biz.SockMultiFile;

import java.io.Serial;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

// todo 并没有完成
// 该类想实现的效果是在json中传输文件,例如 {name:'1',file:[24,35,5,44,55,8,-4]},这个file数组会被解析成bytes映射成文件
public class JsonBinMixedPacket extends JsonBasicPacket implements IFilePacket, IMultiFilePacket, Serializable {

    @Serial
    private static final long serialVersionUID = -8655453456887L;

    // 规定当该key取值是 0的时候代表是单文件上传，1的时候代表多文件上传
    final static String FILE_TYPE_KEY = "@file-type";

    protected SockFile file;
    protected SockMultiFile multiFile;

    public JsonBinMixedPacket(JsonTextBody textBody) {
        this.textBody = textBody;
        byte[] binBody = this.getBinBodyAsBytes();
        if (binBody == null || binBody.length ==0){
            return ;
        }

        JSONObject headers = textBody.getHeaders();
        JSONObject bodyObject = textBody.toJavaObject(JSONObject.class);
        JSONArray jsonFiles = headers.getJSONArray("@JSONFiles");
        /*
        {
            @JSONFiles:[
               {
                path:"$.path",
                type:1,
                name:["name1"],
                range:[start,end]
               }
            ]
        }
        */

        int size = jsonFiles.size();
        for (int i = 0; i < size; i++) {
            JSONObject fileDesc = jsonFiles.getJSONObject(i);
            String path = fileDesc.getString("path");
            int type = fileDesc.getIntValue("type");
            JSONArray name = fileDesc.getJSONArray("name");
            JSONArray range = fileDesc.getJSONArray("range");

            List<SockFile> files = new ArrayList<>();
            for (int j = 0; j < name.size(); j++) {
                int start = range.getIntValue(0);
                int end = range.getIntValue(1);
                int len = end-start+1;
                byte[] fileBytes = new byte[len];
                System.arraycopy(binBody, start, fileBytes, 0, len);
                files.add(new SockFile(fileBytes, name.getString(j)));
            }
            Object file = null;
            if (type == 0){
                // 单文件
                file = files.isEmpty() ? null: files.get(0);
            }else {
                // 多文件
                file = new SockMultiFile(files);
            }
            if (file != null){
                JSONPath.set(bodyObject,path,file);
            }
        }
        textBody.setBody(bodyObject);
    }


    @Override
    public JsonTextBody getTextBodyAsObj() {
        return super.getTextBodyAsObj();
    }

    public JsonBinMixedPacket(JsonTextBody textBody, SockMultiFile files) {
        this.textBody = textBody;
        int totalLen = files.getTotalSizeOfFiles(null);
        ByteBuffer buffer = ByteBuffer.allocate(totalLen);
        JSONArray filenames = new JSONArray();
        JSONArray fileLengths = new JSONArray();
        for (SockFile sockFile : files) {
            filenames.add(sockFile.getFileName());
            fileLengths.add(sockFile.getSize());
            buffer.put(sockFile.getBytes());
        }
        this.binBody = buffer.array();
        this.multiFile = files;

        this.getTextBodyAsObj().setHeader(MULTI_FILENAMES_KEY, filenames);
        this.getTextBodyAsObj().setHeader(MULTI_FILE_LENS_KEY, fileLengths);
        this.getTextBodyAsObj().setHeader(FILE_TYPE_KEY, 1);
    }

    public JsonBinMixedPacket(JsonTextBody textBody, SockFile file) {
        this.textBody = textBody;
        this.file = file;
        this.binBody = file.getBytes();
        this.getTextBodyAsObj().setHeader(FILENAME_KEY, file.getFileName());
        this.getTextBodyAsObj().setHeader(FILE_TYPE_KEY, 0);
    }



    @Override
    public byte protocolVersion() {
        return Protocol.JSON_TEXT_BIN_CONTENT.getVersion();
    }

    @Override
    public String protocolName() {
        return Protocol.JSON_TEXT_BIN_CONTENT.getName();
    }

    @Override
    public SockFile toFile() {
        JSONObject headers = this.getTextBodyAsObj().getHeaders();
        Integer uploadType = headers.getInteger(FILE_TYPE_KEY);
        if (uploadType == null){
            return file;
        }
        if (uploadType ==0){
            //  0代表按照单文件解析方式取二进制内容解析成文件
            if (file != null){
                return file;
            }
            byte[] binBody = getFileBinary();
            if (binBody != null && binBody.length > 0){
                file = new SockFile(binBody, getFilename());
            }
        }else {
            // 多文件解析方式
            SockMultiFile multiFile = this.toMultiFile();
            if (multiFile != null && !multiFile.isEmpty()) {
                file = multiFile.getFile(0);
            }
        }
        return file;
    }

    @Override
    public Map<String, Object> getFileBinaryDesc() {
        return getTextBodyAsObj().getHeaders();
    }

    @Override
    public byte[] getFileBinary() {
        return getBinBodyAsBytes();
    }


    @Override
    public SockMultiFile toMultiFile() {
        if (multiFile == null) {
            JSONObject headers = this.getTextBodyAsObj().getHeaders();
            JSONArray filenames = headers.getObject(MULTI_FILENAMES_KEY,JSONArray.class);
            JSONArray fileLengths = headers.getObject(MULTI_FILE_LENS_KEY,JSONArray.class);
            if (filenames == null || filenames.isEmpty()) {
                return null;
            }
            byte[] binBody = this.getBinBodyAsBytes();
            if (binBody == null || binBody.length ==0){
                return null;
            }
            int pos = 0;
            List<SockFile> files = new ArrayList<>();
            for (int i = 0; i < fileLengths.size(); i++) {
                Integer len = fileLengths.getInteger(i);
                byte[] fileBytes = new byte[len];
                System.arraycopy(binBody, pos, fileBytes, 0, len);
                files.add(new SockFile(fileBytes, filenames.getString(i)));
                pos = pos + len ;
            }
            multiFile = new SockMultiFile(files);
        }
        return multiFile;
    }
}
